﻿#include <QNetworkAccessManager>
#include <QDomDocument>
#include <QFile>
#include <QDir>
#include <QCryptographicHash>
#include <QMessageBox>
#include <QProcess>
#include "widget.h"
#include "ui_widget.h"
#include "httpdownload.h"
#include "log.h"

#define LOG_NAME            "update"
#define DIR_BACKUP_NAME     "backup"
#define DIR_DOWNLOAD_NAME   "tmp"
#define DIR_BACKUP_PATH     "./backup/"
#define DIR_DOWNLOAD_PATH   "./tmp/"

QString createDirWithName(const QString &dir_name){
    //QDir默认构造为当前路径，当创建是以当前路径为开始创建的
    //macos的运行方式和windows是有区别的。
#ifdef Q_OS_WIN32
    QDir dir = QDir::currentPath() + "\\" +dir_name;
    QStringList list = dir.absolutePath().split("\\");
    QString mid = "\\";
#else
    QDir dir = QDir::currentPath() + "/" +dir_name;
    QStringList list = dir.absolutePath().split("/");
    QString mid = "/";
#endif
    if(dir.exists()){
        return dir.absolutePath();
    }
    QString path;
    for(QStringList::const_iterator it = list.begin(); it != list.end(); ++it){
        path += *it;
        path += mid;
        if(!dir.exists(path))
            if(!dir.mkdir(path)){
                return "";
            }
    }
    return dir.absolutePath();
}

QString getHashMd5(const QString &filePath){
    QString md5;
    QFile file(filePath);
    if(file.open(QIODevice::ReadOnly)){
        QByteArray array = QCryptographicHash::hash(file.readAll(),QCryptographicHash::Md5);
        md5 = QString(array.toHex());
    }
    file.close();
    return md5;
}

Widget::Widget(const QStringList &argv, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    show();
    TKGLog::Instance()->set_file_name(LOG_NAME);
    connect(ui->pb_cancel,SIGNAL(clicked(bool)),this,SLOT(slot_cancel()));
    connect(ui->pb_start,SIGNAL(clicked(bool)),this,SLOT(slot_start()));
    connect(ui->pb_update,SIGNAL(clicked(bool)),this,SLOT(slot_update()));
    ui->pb_start->hide();
    ui->pb_update->hide();
    if(argv.size() >= 10){
        m_ip = argv[1];
        m_port = argv[2].toInt();
        m_version_name = argv[3];
        m_version_num = argv[4];
        m_group = argv[5];
        m_user = argv[6];
        m_pass = argv[7];
        m_client_name = argv[8];
        setWindowTitle(argv[9]);
        ui->progressBar->hide();
    }else{
        exit(0);
    }

#ifdef QT_NO_DEBUG
#else
    ui->plainTextEdit->appendPlainText(QDir::currentPath());
    ui->plainTextEdit->appendPlainText("ip:"+ m_ip);
    ui->plainTextEdit->appendPlainText("port:"+ QString::number(m_port));
    ui->plainTextEdit->appendPlainText("version:"+ m_version_name);
    ui->plainTextEdit->appendPlainText("group:"+ m_group);
    ui->plainTextEdit->appendPlainText("user:"+ m_user);
    ui->plainTextEdit->appendPlainText("pass:"+ m_pass);
    ui->plainTextEdit->appendPlainText("client:"+ m_client_name);
#endif

    QDir tmp_dir(DIR_DOWNLOAD_NAME),backup_dir(DIR_BACKUP_NAME);
    if(!tmp_dir.exists()){
        createDirWithName(DIR_DOWNLOAD_NAME);
    }
    if(!backup_dir.exists()){
        createDirWithName(DIR_BACKUP_NAME);
    }
    m_manager = new QNetworkAccessManager(0);
}

Widget::~Widget(){
    delete ui;
}

QString Widget::md5(const QString &file_name){
    return getHashMd5(DIR_DOWNLOAD_PATH + file_name);
}

bool Widget::parseVersionFile(){
    QFile file("new_"+m_version_name);
    QDomDocument doc;
    if(!file.open(QIODevice::ReadOnly | QFile::Text)){
        TKGwrite_log_info(Q_CS(QString("打开文件失败")<< file.errorString()));
        return false;
    }else{
        QByteArray content = file.readAll();
        file.close();
#ifdef QT_NO_DEBUG
#else
        ui->plainTextEdit->appendPlainText(content);
#endif
        if(doc.setContent(content)){
            QDomElement root = doc.documentElement();
            if(root.tagName() == "update"){
                //一些头信息
                QDomNode file_version = root.elementsByTagName("version").at(0);
                QDomNode file_detail = root.elementsByTagName("detail").at(0);
                QDomNode file_time = root.elementsByTagName("time").at(0);
                ui->plainTextEdit->appendPlainText("当前版本：");
                ui->plainTextEdit->appendPlainText("最新版本:" + file_version.toElement().text());
                ui->plainTextEdit->appendPlainText("更新时间："+file_time.toElement().text());
                ui->plainTextEdit->appendPlainText("更新内容：");
                ui->plainTextEdit->appendPlainText(file_detail.toElement().text());
                ui->pb_update->show();
            }else{
                ui->plainTextEdit->appendPlainText("文件解析错误，请退出");
                ui->pb_cancel->setText("退出");
                TKGwrite_log_error(Q_CS(QString("tag错误")));
                goto FAILED;
            }
        }else{
            TKGwrite_log_error(Q_CS(QString("xml文件错误") << m_version_name));
            ui->plainTextEdit->appendPlainText("文件解析错误，请退出");
            ui->pb_cancel->setText("退出");
            goto FAILED;
        }
    }
SUCCESS:
    return true;
FAILED:
    TKGwrite_log_error(Q_CS(QString("升级失败！")));
    ui->plainTextEdit->appendPlainText("升级失败！");
    return false;
}

bool Widget::downloadFile(){
    QFile file("new_"+m_version_name);
    QDomDocument doc;
    if(!file.open(QIODevice::ReadOnly | QFile::Text)){
        TKGwrite_log_info(Q_CS(QString("打开文件失败")<< file.errorString()));
        return false;
    }else{
        QByteArray content = file.readAll();
        file.close();
#ifdef QT_NO_DEBUG
#else
        ui->plainTextEdit->appendPlainText(content);
#endif
        if(doc.setContent(content)){
            QDomElement root = doc.documentElement();
            HttpDownload downer(this);
            if(root.tagName() == "update"){
                QDomNodeList file_list= root.firstChildElement("filelist").elementsByTagName("file");
                ui->plainTextEdit->appendPlainText("--------------------------------");
                ui->plainTextEdit->appendPlainText("此次升级共需下载"+QString::number(file_list.size())+"个文件！");
                ui->progressBar->show();
                ui->lb_status->setText("正在升级...");
                for(int i = 0; i < file_list.size(); ++i){
                    QString file_name = file_list.at(i).toElement().attribute("name");
                    ui->plainTextEdit->appendPlainText("正在下载第"+QString::number(i+1)+"个文件【"+file_name+"】,请等待...");
                    QString file_dir = file_list.at(i).toElement().attribute("dir");
                    QString file_md5 = file_list.at(i).toElement().attribute("md5");
                    int recv_total = downer.downloadFile(m_ip+":"+QString::number(m_port)+"/file/"+m_group+"/"+m_version_num+"/",file_name,"tmp");
                    ui->plainTextEdit->appendPlainText("文件大小:"+QString::number(recv_total));
                    if(!recv_total)
                        goto FAILED;
                    ui->plainTextEdit->appendPlainText("下载成功!");
                    QString md5_v = md5(file_name);
                    if( file_md5 != md5_v ){
                        ui->plainTextEdit->appendPlainText("校验失败【"+file_name+"】!"+file_md5 + " "+md5_v);
                        goto FAILED;
                    }else{
                        ui->plainTextEdit->appendPlainText("校验成功【"+file_name+"】!");
                        m_down_files << file_name;//校验成功的推入
                    }
                }
                ui->progressBar->hide();
                ui->plainTextEdit->appendPlainText("下载全部完成!");
                ui->lb_status->setText("升级完成");
                TKGwrite_log_info(Q_CS(QString("m_down_files ") << m_down_files));
                goto SUCCESS;
            }else{
                TKGwrite_log_error(Q_CS(QString("tag错误")));
                goto FAILED;
            }
        }else{
            TKGwrite_log_error(Q_CS(QString("xml文件错误") << m_version_name));
            goto FAILED;
        }
    }
SUCCESS:
    return true;
FAILED:
    TKGwrite_log_error(Q_CS(QString("升级失败！")));
    ui->plainTextEdit->appendPlainText("升级失败！");
    return false;
}

void Widget::slot_update(){
    ui->pb_update->setDisabled(true);
    if( downloadFile() && backupToBackupFiles() && delMainFiles() && copyToMainFiles()){
        ui->plainTextEdit->appendPlainText("升级成功");
        ui->pb_start->show();
        ui->pb_update->hide();
    }else{
        ui->plainTextEdit->appendPlainText("升级失败!!!");
    }
    ui->pb_update->setEnabled(true);
}

void Widget::slot_start(){
    QProcess * exe = new QProcess(0);
    exe->start(m_client_name);
    exe->waitForStarted();
    if(exe->state() == QProcess::Running){
        exit(0);
    }else{
        ui->plainTextEdit->appendPlainText("启动失败:"+exe->errorString());
    }
}

void Widget::slot_cancel(){
    QProcess * exe = new QProcess(0);
    exe->start(m_client_name);
    exe->waitForStarted();
    if(exe->state() == QProcess::Running){
        exit(0);
    }else{
        ui->plainTextEdit->appendPlainText("启动失败:"+exe->errorString());
    }
}

void Widget::exec(){
    parseVersionFile();
}

bool Widget::delFiles(const QStringList &files, const QString &dir){
    if(dir.isEmpty()){
        foreach (QString file_name,files){
            QFile::remove(file_name);
        }
    }else {
        foreach (QString file_name,files){
            QFile::remove(dir+file_name);
        }
    }
    return true;
}

bool Widget::backupToBackupFiles(){
    foreach (QString file_name,m_down_files ) {
        if(!QFile(file_name).exists())
            continue;
        if(!QFile::copy(file_name,DIR_BACKUP_PATH+file_name)){
            delFiles(m_back_ok_list,DIR_BACKUP_PATH);
            return false;
        }
        m_back_ok_list << file_name;
    }
    TKGwrite_log_info(Q_CS(QString("m_back_ok_list") << m_back_ok_list));
    return true;
}

bool Widget::delMainFiles(){
    foreach (QString file_name, m_back_ok_list) {
        if(!QFile::remove(file_name)){
            // 删除失败，需要把已删除的还原回来
            foreach(QString del_file,m_del_ok_list){
                QFile::copy(DIR_BACKUP_PATH+del_file,del_file);
            }
            // 删除已备份的
            delFiles(m_back_ok_list,DIR_BACKUP_PATH);
            // 删除已下载的,这里可以作为以后升级使用。当文件夹已经包含该文件，并且校验成功，那么不需要再重新下载了！
            //delFiles(m_down_files,DIR_DOWNLOAD_PATH);
            return false;
        }
        m_del_ok_list << file_name;
    }
    TKGwrite_log_info(Q_CS(QString("m_del_ok_list") << m_del_ok_list));
    return true;
}

bool Widget::copyToMainFiles(){
    foreach(QString file_name,m_down_files){
        if(!QFile::copy(DIR_DOWNLOAD_PATH+file_name,file_name)){
            // 删除已替换过的文件
            delFiles(m_copy_ok_list);

            // 还原全部已删除的文件
            foreach(QString delfile,m_del_ok_list){
                QFile::copy(DIR_BACKUP_PATH+delfile,delfile);
            }

            // 删除已备份的
            delFiles(m_back_ok_list,DIR_BACKUP_PATH);
            return false;
        }
        m_copy_ok_list << file_name;
    }
    QFile::remove(m_version_name);
    QFile::rename("new_"+m_version_name,m_version_name);
    delFiles(m_back_ok_list,DIR_BACKUP_PATH);
    delFiles(m_down_files,DIR_DOWNLOAD_PATH);
    return true;
}

void Widget::closeEvent(QCloseEvent *e){
    exit(0);
}
